package com.ustc.py.practice.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ustc.py.learn.constant.KnowledgeTypeEnum;
import com.ustc.py.practice.entity.Submit;
import com.ustc.py.practice.entity.SubmitDTO;
import com.ustc.py.practice.entity.TestData;
import com.ustc.py.practice.mapper.SubmitMapper;
import com.ustc.py.practice.service.SubmitService;
import com.ustc.py.practice.service.TestDataService;
import com.ustc.py.storage.LoginUserLocalStorage;
import com.ustc.py.utils.CodeUtil;
import com.ustc.py.utils.OjConstant;
import com.ustc.py.utils.OjJudge0Util;
import com.ustc.py.utils.R;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class SubmitServiceImpl extends ServiceImpl<SubmitMapper, Submit> implements SubmitService {
    @Autowired
    private TestDataService testDataService;
    @Autowired
    private CodeUtil codeUtil;
    @Autowired
    private OjJudge0Util ojJudge0Util;
    @Autowired
    private SubmitMapper submitMapper;

    @Override
    public R submit(SubmitDTO submitDTO) throws UnsupportedEncodingException {
        int algorithmId = submitDTO.problemId;
        List<TestData> testDataList = testDataService.query().eq("algorithm_id", algorithmId).list();
        double time=0;
        double space=0;
        boolean flag=true;
        for (TestData testData : testDataList) {
            try {
                String  submissionToken = ojJudge0Util.submitCode(OjConstant.LANGUAGE_PYTHON3, submitDTO.code, codeUtil.encode(testData.getStdin()), codeUtil.encode(testData.getStdout()));
                Response submission = ojJudge0Util.getSubmission(submissionToken);
                String body = submission.body().string();
                JSONObject subJson = JSONObject.parseObject(body);
                int status_id = (int) subJson.get("status_id");
                if(status_id!=OjConstant.STATUS_ACCEPTED) {
                    //TODO: 真实运行时要记录
                    //long userId = LoginUserLocalStorage.getCurrentLoginUserId();
                    Submit submit = new Submit();
                    //submit.setUserId((int)userId);
                    submit.setProblemId(submitDTO.problemId);
                    // Fixed: 数据库应存储 base64编码的code
                    submit.setCode(submitDTO.code);
                    submit.setStatus(status_id);
                    submit.setTimeConsumption("N/A");
                    submit.setSpaceConsumption("N/A");
                    save(submit);

                    // Fixed: 统一返回结构
                    Map<String, Object> data = new HashMap<>();
                    data.put("status_id", status_id);
                    data.put("status_msg", OjConstant.status.get(status_id));
                    data.put("time consumption", submit.getTimeConsumption());
                    data.put("space consumption", submit.getSpaceConsumption());
                    return R.jsonify(500, "运行失败", data);
                }
                if(subJson.getString("time")!=null)
                    time+=Double.valueOf(subJson.getString("time"));

                space+=(int)subJson.get("memory");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        //TODO: 真实运行时要记录
        //long userId = LoginUserLocalStorage.getCurrentLoginUserId();
        Submit submit = new Submit();
        //submit.setUserId((int)userId);
        submit.setProblemId(submitDTO.problemId);
        submit.setCode(submitDTO.code);
        submit.setStatus(3);
        submit.setTimeConsumption((time/testDataList.size()) +"");
        submit.setSpaceConsumption(space/testDataList.size() +"");
        boolean result = save(submit);
        if(result){
            // Fixed: 统一返回结构
            Map<String, Object> data = new HashMap<>();
            data.put("status_id", OjConstant.STATUS_ACCEPTED);
            data.put("status_msg", OjConstant.status.get(OjConstant.STATUS_ACCEPTED));
            data.put("time consumption", submit.getTimeConsumption()+"s");
            data.put("space consumption", submit.getSpaceConsumption()+"KB");

            return R.jsonify(200, "本题运行成功", data);
        }
        return R.jsonify(500, "提交失败,请重试", null);
    }

    /**
     * 提交numpy相关的python代码
     * @param submitDTO
     * @return
     * Jackiehan is in charge.
     */
    @Override
    public R submitNumpy(SubmitDTO submitDTO) {
        try {
            // call api
            String submissionToken = ojJudge0Util.submitCode(
                    OjConstant.LANGUAGE_PYTHON3,
                    submitDTO.code,
                    null,
                    null);
            Response submission = ojJudge0Util.getSubmission(submissionToken);

            // process result
            String body = submission.body().string();
            JSONObject subJson = JSONObject.parseObject(body);
            int status_id = (int) subJson.get("status_id");

            Submit submit = new Submit();
            submit.setProblemId(null);      // numpy代码不对应题目编号，置null
            submit.setCode(submitDTO.code);
            submit.setStatus(status_id);
            //TODO: 真实运行时要记录
            //long userId = LoginUserLocalStorage.getCurrentLoginUserId();
            //submit.setUserId((int)userId);

            // failed
            if (status_id != OjConstant.STATUS_ACCEPTED) {
                submit.setTimeConsumption("N/A");
                submit.setSpaceConsumption("N/A");
                save(submit);

                Map<String, Object> data = new HashMap<>();
                data.put("status_id", status_id);
                data.put("status_msg", OjConstant.status.get(status_id));
                data.put("time consumption", submit.getTimeConsumption());
                data.put("space consumption", submit.getSpaceConsumption());
                return R.jsonify(500, "运行失败", data);
            }
            else{   // succeed
                double time=0;
                double space=0;
                if (subJson.getString("time") != null) {
                    time = Double.valueOf(subJson.getString("time"));
                }
                space = (int) subJson.get("memory");
                save(submit);

                // 获取运行结果 需要前端解析 base64
                String stdout = subJson.getString("stdout");
                // 构造返回结果
                Map<String, Object> data = new HashMap<>();
                data.put("status_id", status_id);
                data.put("status_msg", OjConstant.status.get(status_id));
                data.put("stdout", stdout);
                data.put("time consumption", time+"s");
                data.put("space consumption", space+"KB");

                return R.jsonify(200, "运行成功", data);
            }
        }
        catch(IOException e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取用户的代码提交记录
     *
     * @param type param参数     沿用Knowledge处的type类型划分
     *             1-algorithm   获取算法题提交记录
     *             2-numpy       获取numpy学习代码提交记录
     *             ----------------------------------------
     *             其他值或未传    获取当前用户的所有代码提交记录
     * @return Jackiehan is in charge.
     */
    @Override
    public R getRecords(Integer type) {
        LambdaQueryWrapper<Submit> wrapper = new LambdaQueryWrapper<>();
        long userId = LoginUserLocalStorage.getCurrentLoginUserId();

        // 条件：当前用户
        wrapper.eq(Submit::getUserId, userId);

        // 条件：何种类型
        if(type != null && type == KnowledgeTypeEnum.ALGORITHM.getTypeId()){
            wrapper.isNotNull(Submit::getProblemId);
        }else if(type != null && type == KnowledgeTypeEnum.NUMPY.getTypeId()){
            wrapper.isNull(Submit::getProblemId);
        }
        wrapper.orderByDesc(Submit::getId);

        List<Submit> submitList = submitMapper.selectList(wrapper);
        return R.jsonify(200, "OK", submitList);
    }
}
